import java.io.File;
import java.io.FileOutputStream;

/*
 * PTS - Personal Taxonomy Similarity
 * If an item is a track, take the mean of tracks of the same album also rated 
 * by the user.
 * If an item is an album or artist, take the mean of the tracks on that 
 * album/artist that the user have rated. 
 */
public class APTS extends Scheduler implements Runnable {
	int start;
	int stop;
	byte[] BLOCK;
	
	final double NONE = -0.1;

	public APTS(int start, int stop) {
		this.start = start;
		this.stop = stop;
	}

	void writeBlock() {
		String FILE_NAME = String.format("%s%07d.bin", BLOCK_PATH, start);
		File out = new File(FILE_NAME);
		FileOutputStream fos;
		try {
			fos = new FileOutputStream(out);
			fos.write(BLOCK);
		} catch (Exception e) {
			e.printStackTrace();
		}

		System.out.println("Done writing: " + FILE_NAME + " @ "
				+ this.getClass());
	}

	@Override
	public void run() {
		int ilen = USERB[0].ilen;
		BLOCK = new byte[(stop - start) * ilen];

		int c = 0;
		for (int bUserId = start; bUserId < stop; bUserId++) {
			for (int i = 0; i < ilen; i++) {
				int bItemId = USERB[bUserId].items[i];
				byte pred = predict(bUserId, bItemId);

				BLOCK[c * ilen + i] = pred;
			}
			c++;
		}
		writeBlock();
	}

	private byte predict(int bUserId, int bItemId) {
		double mean = 0;
		Item bItem = ITEMA[bItemId];
		if (bItem instanceof Track) {
			Track bTrack = (Track) bItem;
			Album bItemAlbum = bTrack.album;
			if (bItemAlbum != null) {
				int[] itemIds = USERA[bUserId].items;
				mean = getAlbumMean(itemIds, bItemAlbum);
				if (!Double.isNaN(mean)) {
					return (byte) mean;
				} else {
					return (byte) bItemAlbum.mean;
				}
			}
		} else if (bItem instanceof Album) {
			Album bAlbum = (Album) bItem;
			int[] itemIds = USERA[bUserId].items;
			mean = getAlbumMean(itemIds, bAlbum);
			if (!Double.isNaN(mean)) {
				return (byte) mean;
			} else {
				return (byte) bAlbum.mean;
			}

		} else if (bItem instanceof Artist) {
			Artist bArtist = (Artist) bItem;
			int[] itemIds = USERA[bUserId].items;
			mean = getArtistMean(itemIds, bArtist);
			if (!Double.isNaN(mean)) {
				return (byte) mean;
			} else {
				return (byte) bArtist.mean;
			}
		}
		return (byte) USERA[bUserId].mean;
	}

	private double getAlbumMean(int[] itemIds, Album bItemAlbum) {
		double albumMean = 0;
		boolean set = false;
		for (int itemID : itemIds) {
			if (ITEMA[itemID] instanceof Track
					&& bItemAlbum.equals(((Track) ITEMA[itemID]).album)) {
				albumMean += ITEMA[itemID].mean;
				set = true;
			}
		}
		if (set) {
			return albumMean;
		} else {
			return Double.NaN;
		}
	}

	private double getArtistMean(int[] itemIds, Artist bItemArtist) {
		double artistMean = 0;
		boolean set = false;
		for (int itemID : itemIds) {
			if (ITEMA[itemID] instanceof Album
					&& bItemArtist.equals(((Album) ITEMA[itemID]).artist)) {
				artistMean += ITEMA[itemID].mean;
				set = true;
			}
		}
		if (set) {
			return artistMean;
		} else {
			return Double.NaN;
		}
	}
}
